package dao;

import anno.Column;
import anno.Table;
import utils.JDBCUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 基础DAO接口
 */
interface BaseDao<T> {
    /**
     * 插入实体
     *
     * @param entity 实体对象
     * @return 插入后的自增主键
     */
    Integer insert(T entity) throws SQLException;

    /**
     * 批量插入实体
     *
     * @param entities 实体列表
     * @return 插入行数数组
     */
    int[] batchInsert(List<T> entities) throws SQLException;

    /**
     * 根据ID查询实体
     *
     * @param id 主键ID
     * @return 实体对象，不存在则返回null
     */
    T findById(Integer id) throws SQLException;

    /**
     * 查询所有记录
     *
     * @return 实体对象列表
     */
    List<T> findAll() throws SQLException;

    /**
     * 更新实体
     *
     * @param entity 实体对象
     * @return 是否更新成功
     */
    boolean update(T entity) throws SQLException;

    /**
     * 根据ID删除记录
     *
     * @param id 主键ID
     * @return 是否删除成功
     */
    boolean deleteById(Integer id) throws SQLException;

    /**
     * 批量删除记录
     *
     * @param ids ID集合
     * @return 删除的记录数
     */
    int deleteByIds(Collection<Integer> ids) throws SQLException;

    /**
     * 统计总记录数
     *
     * @return 总记录数
     */
    long count() throws SQLException;

    /**
     * 检查字段值是否存在
     *
     * @param field 字段名（驼峰命名）
     * @param value 字段值
     * @return 是否存在
     */
    boolean exists(String field, Object value) throws SQLException;

    /**
     * 根据指定字段查询实体列表
     *
     * @param field 字段名（驼峰命名）
     * @param value 字段值
     * @return 符合条件的实体列表
     */
    List<T> findByField(String field, Object value) throws SQLException;

    /**
     * 根据指定字段查询单个实体
     * 当存在多个结果时返回第一个
     *
     * @param field 字段名（驼峰命名）
     * @param value 字段值
     * @return 符合条件的实体，如果不存在则返回null
     */
    T findOneByField(String field, Object value) throws SQLException;
}


/**
 * 抽象基础DAO实现
 */
public abstract class AbstractBaseDao<T> implements BaseDao<T> {

    /**
     * 已经实现的功能：
     * 1. 自动处理表名（驼峰转下划线）
     * 2. 自动识别和处理主键字段（支持@Column注解和默认'id'字段）
     * 3. 基础CRUD操作：
     * - 插入（insert(T entity)）
     * - 批量插入（batchInsert(List<T> entities)）
     * - 根据ID查询（findById(ID id)）
     * - 查询所有（findAll()）
     * - 更新（update(T entity)）
     * - 根据ID删除（deleteById(ID id)）
     * - 批量删除（deleteByIds(List<ID> ids)）
     * - 统计总数（count()）
     * - 字段值存在性检查（exists(String field, Object value)） ，根据指定字段查询记录是否存在
     * - 根据指定字段查询列表（findByField(String field, Object value)）
     * - 根据指定字段查询单个实体（findOneByField(String field, Object value)）
     * 4. 自动映射ResultSet到实体对象
     * 5. 支持@Column注解的字段映射和忽略
     * 6. 自动处理数据库连接的获取和释放
     * 7. LocalDateTime类型的自动转换
     * -------
     * 继承后无需实现的方法：
     * 1. 所有BaseDao接口中定义的基础CRUD方法
     * 2. 实体与数据库字段的映射转换
     * 3. 数据库连接的管理
     * -------
     * 继承此类只需：
     * 1. 指定实体类型参数T
     * 2. 实现特定的业务查询方法（如果需要）
     * -------
     * 方法参数说明：
     * - T entity: 实体对象
     * - List<T> entities: 实体对象列表
     * - ID id: 主键值
     * - List<ID> ids: 主键值列表
     * - String field: 字段名（驼峰命名）
     * - Object value: 字段值
     */

    protected final Class<T> entityClass;
    protected final String tableName;
    protected final Field idField;  // 缓存主键字段

    @FunctionalInterface
    protected interface DatabaseOperation<R> {
        R execute() throws SQLException;
    }

    @SuppressWarnings("unchecked")
    public AbstractBaseDao() {
        this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        this.tableName = getTableName(); // 使用新的getTableName方法
        this.idField = findIdField();
    }

    /**
     * 获取表名的方法修改
     */
    private String getTableName() {
        // 1. 检查@Table注解
        Table tableAnno = entityClass.getAnnotation(Table.class);
        if (tableAnno != null && !tableAnno.value().isEmpty()) {
            return tableAnno.value();
        }
        // 2. 如果没有@Table注解或value为空，使用默认的驼峰转下划线
        return camelToUnderline(entityClass.getSimpleName());
    }

    @Override
    public List<T> findByField(String field, Object value) throws SQLException {
        return executeWithConnection(() -> {
            String columnName = camelToUnderline(field);
            String sql = String.format("SELECT * FROM %s WHERE %s = ?", tableName, columnName);
            List<T> results = new ArrayList<>();
            ResultSet rs = (ResultSet) JDBCUtils.execute(sql, value);
            while (rs.next()) {
                results.add(mapResultSetToEntity(rs));
            }
            return results;
        });
    }

    @Override
    public T findOneByField(String field, Object value) throws SQLException {
        return executeWithConnection(() -> {
            String columnName = camelToUnderline(field);
            String sql = String.format("SELECT * FROM %s WHERE %s = ? LIMIT 1", tableName, columnName);
            ResultSet rs = (ResultSet) JDBCUtils.execute(sql, value);
            return rs.next() ? mapResultSetToEntity(rs) : null;
        });
    }

    /**
     * 查找主键字段
     */
    private Field findIdField() {
        List<Field> allFields = getAllFields(entityClass);

        // 1. 首先查找带@Column(isId=true)的字段
        Optional<Field> idField = allFields.stream().filter(field -> {
            Column column = field.getAnnotation(Column.class);
            return column != null && column.isId();
        }).findFirst();

        if (idField.isPresent()) {
            return idField.get();
        }

        // 2. 查找名为"id"的字段
        Optional<Field> defaultIdField = allFields.stream().filter(field -> field.getName().equals("id")).findFirst();

        if (defaultIdField.isPresent()) {
            return defaultIdField.get();
        }

        // 3. 都没有找到，抛出异常
        throw new IllegalStateException(String.format("在实体类 %s 中未找到主键字段。\n" + "请使用以下方式之一：\n" + "1. 在主键字段上添加@Column(isId = true)注解\n" + "    例如：@Column(isId = true) private Integer userId;\n" + "2. 或者直接使用id作为字段名\n" + "    例如：private Integer id;", entityClass.getName()));
    }

    /**
     * 获取字段对应的数据库列名
     */
    protected String getColumnName(Field field) {
        Column column = field.getAnnotation(Column.class);
        if (column != null) {
            // 如果字段被标记为忽略，返回null
            if (column.ignore()) {
                return null;
            }
            // 如果指定了列名，使用指定的列名
            if (!column.value().isEmpty()) {
                return column.value();
            }
        }
        // 默认使用驼峰转下划线
        return camelToUnderline(field.getName());
    }

    /**
     * 判断字段是否被忽略
     */
    protected boolean isFieldIgnored(Field field) {
        Column column = field.getAnnotation(Column.class);
        return column != null && column.ignore();
    }

    /**
     * 获取所有非忽略字段
     */
    protected List<Field> getNonIgnoredFields() {
        return getAllFields(entityClass).stream().filter(field -> !isFieldIgnored(field)).collect(Collectors.toList());
    }

    /**
     * 执行数据库操作并确保连接被关闭的模板方法
     */
    protected <R> R executeWithConnection(DatabaseOperation<R> operation) throws SQLException {
        try {
            R result = operation.execute();
            JDBCUtils.closeConnection();
            return result;
        } catch (SQLException e) {
            e.printStackTrace();
            throw e;
        } finally {
            System.out.println("已经释放connect....【AbstractBaseDao】");
            JDBCUtils.closeConnection();
        }
    }

    @Override
    public Integer insert(T entity) throws SQLException {
        return executeWithConnection(() -> {
            List<Field> fields = getNonIgnoredFields().stream().filter(field -> !field.equals(idField)).collect(Collectors.toList());

            StringJoiner columns = new StringJoiner(",");
            StringJoiner placeholders = new StringJoiner(",");
            List<Object> params = new ArrayList<>();

            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    Object value = field.get(entity);
                    if (value != null) {
                        columns.add(getColumnName(field));
                        placeholders.add("?");
                        params.add(value);
                    }
                } catch (IllegalAccessException e) {
                    throw new SQLException("字段访问失败: " + field.getName(), e);
                }
            }

            String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, columns.toString(), placeholders.toString());

            JDBCUtils.execute(sql, params.toArray());
            ResultSet rs = (ResultSet) JDBCUtils.execute("SELECT LAST_INSERT_ID()");
            if (rs.next()) {
                Integer generatedId = rs.getInt(1);
                // 设置生成的ID到实体
                setIdValue(entity, generatedId);
                return generatedId;
            }
            throw new SQLException("获取自增主键失败");
        });
    }

    @Override
    public int[] batchInsert(List<T> entities) throws SQLException {
        return executeWithConnection(() -> {
            if (entities == null || entities.isEmpty()) {
                return new int[0];
            }

            T firstEntity = entities.stream().filter(Objects::nonNull).findFirst().orElseThrow(() -> new SQLException("实体列表中没有有效实体"));

            List<Field> fields = getNonIgnoredFields().stream().filter(field -> !field.equals(idField)).collect(Collectors.toList());

            StringJoiner columns = new StringJoiner(",");
            StringJoiner placeholders = new StringJoiner(",");
            List<Field> validFields = new ArrayList<>();

            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    if (field.get(firstEntity) != null) {
                        validFields.add(field);
                        columns.add(getColumnName(field));
                        placeholders.add("?");
                    }
                } catch (IllegalAccessException e) {
                    throw new SQLException("字段访问失败: " + field.getName(), e);
                }
            }

            String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, columns.toString(), placeholders.toString());

            Connection conn = JDBCUtils.getConnection();
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                conn.setAutoCommit(false);

                for (T entity : entities) {
                    if (entity != null) {
                        int paramIndex = 1;
                        for (Field field : validFields) {
                            field.setAccessible(true);
                            Object value = field.get(entity);
                            stmt.setObject(paramIndex++, value);
                        }
                        stmt.addBatch();
                    }
                }

                int[] result = stmt.executeBatch();
                conn.commit();
                return result;
            } catch (Exception e) {
                conn.rollback();
                throw new SQLException("批量插入失败", e);
            } finally {
                conn.setAutoCommit(true);
            }
        });
    }

    @Override
    public T findById(Integer id) throws SQLException {
        return executeWithConnection(() -> {
            String sql = String.format("SELECT * FROM %s WHERE %s = ?", tableName, getColumnName(idField));
            ResultSet rs = (ResultSet) JDBCUtils.execute(sql, id);
            return rs.next() ? mapResultSetToEntity(rs) : null;
        });
    }

    @Override
    public List<T> findAll() throws SQLException {
        return executeWithConnection(() -> {
            String sql = String.format("SELECT * FROM %s", tableName);
            List<T> results = new ArrayList<>();
            ResultSet rs = (ResultSet) JDBCUtils.execute(sql);
            while (rs.next()) {
                results.add(mapResultSetToEntity(rs));
            }
            return results;
        });
    }

    @Override
    public boolean update(T entity) throws SQLException {
        return executeWithConnection(() -> {
            List<Field> fields = getNonIgnoredFields().stream().filter(field -> !field.equals(idField)).collect(Collectors.toList());

            StringJoiner setClause = new StringJoiner(",");
            List<Object> params = new ArrayList<>();

            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    Object value = field.get(entity);
                    if (value != null) {
                        setClause.add(getColumnName(field) + "=?");
                        params.add(value);
                    }
                } catch (IllegalAccessException e) {
                    throw new SQLException("字段访问失败: " + field.getName(), e);
                }
            }

            Object idValue = getIdValue(entity);
            if (idValue == null) {
                throw new SQLException("更新操作必须提供ID值");
            }

            params.add(idValue);
            String sql = String.format("UPDATE %s SET %s WHERE %s=?", tableName, setClause.toString(), getColumnName(idField));

            return (Integer) JDBCUtils.execute(sql, params.toArray()) > 0;
        });
    }

    @Override
    public boolean deleteById(Integer id) throws SQLException {
        return executeWithConnection(() -> {
            String sql = String.format("DELETE FROM %s WHERE %s=?", tableName, getColumnName(idField));
            return (Integer) JDBCUtils.execute(sql, id) > 0;
        });
    }

    @Override
    public int deleteByIds(Collection<Integer> ids) throws SQLException {
        return executeWithConnection(() -> {
            if (ids == null || ids.isEmpty()) {
                return 0;
            }

            String sql = String.format("DELETE FROM %s WHERE %s IN (%s)", tableName, getColumnName(idField), String.join(",", Collections.nCopies(ids.size(), "?")));

            return (Integer) JDBCUtils.execute(sql, ids.toArray());
        });
    }

    @Override
    public long count() throws SQLException {
        return executeWithConnection(() -> {
            String sql = String.format("SELECT COUNT(*) FROM %s", tableName);
            ResultSet rs = (ResultSet) JDBCUtils.execute(sql);
            return rs.next() ? rs.getLong(1) : 0;
        });
    }

    @Override
    public boolean exists(String field, Object value) throws SQLException {
        return executeWithConnection(() -> {
            String columnName = camelToUnderline(field);
            String sql = String.format("SELECT 1 FROM %s WHERE %s = ? LIMIT 1", tableName, columnName);
            ResultSet rs = (ResultSet) JDBCUtils.execute(sql, value);
            return rs.next();
        });
    }

    /**
     * 获取ID值
     */
    protected Object getIdValue(T entity) throws SQLException {
        try {
            idField.setAccessible(true);
            return idField.get(entity);
        } catch (IllegalAccessException e) {
            throw new SQLException("获取ID字段值失败", e);
        }
    }

    /**
     * 设置ID值
     */
    protected void setIdValue(T entity, Object value) throws SQLException {
        try {
            idField.setAccessible(true);
            idField.set(entity, value);
        } catch (IllegalAccessException e) {
            throw new SQLException("设置ID字段值失败", e);
        }
    }

    /**
     * 将ResultSet映射为实体对象
     */
    protected T mapResultSetToEntity(ResultSet rs) throws SQLException {
        try {
            T entity = entityClass.getDeclaredConstructor().newInstance();

            for (Field field : getAllFields(entityClass)) {
                if (isFieldIgnored(field)) {
                    continue;
                }
                field.setAccessible(true);
                String columnName = field.equals(idField) ? getColumnName(idField) : getColumnName(field);

                try {
                    Object value = rs.getObject(columnName);
                    if (value != null) {
                        // 处理LocalDateTime类型
                        if (field.getType() == LocalDateTime.class && value instanceof Timestamp) {
                            value = ((Timestamp) value).toLocalDateTime();
                        }
                          // 只处理Integer和Long封装类型的转换
                        else if (field.getType() == Integer.class && value instanceof Long) {
                            value = Integer.valueOf(String.valueOf(value));
                        } else if (field.getType() == Long.class && value instanceof Integer) {
                            value = Long.valueOf(String.valueOf(value));
                        }
                        field.set(entity, value);
                    }
                } catch (SQLException e) {
                    // 忽略不存在的列
                    continue;
                }
            }

            return entity;
        } catch (ReflectiveOperationException e) {
            throw new SQLException("结果集映射到实体失败", e);
        }
    }

    /**
     * 获取所有字段（包括父类字段）
     */
    protected List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new ArrayList<>();
        while (type != null && type != Object.class) {
            fields.addAll(Arrays.asList(type.getDeclaredFields()));
            type = type.getSuperclass();
        }
        return fields;
    }

    /**
     * 驼峰命名转下划线命名
     */
    protected String camelToUnderline(String camel) {
        if (camel == null || camel.isEmpty()) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        result.append(Character.toLowerCase(camel.charAt(0)));

        for (int i = 1; i < camel.length(); i++) {
            char ch = camel.charAt(i);
            if (Character.isUpperCase(ch)) {
                result.append('_');
                result.append(Character.toLowerCase(ch));
            } else {
                result.append(ch);
            }
        }
        return result.toString();
    }
}